#include <stdio.h>
#include <pcap.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <time.h>

// ICMP ECHO请求报文处理器
void icmp_packet_handler(u_char *user_data, const struct pcap_pkthdr *pkthdr, const u_char *packet)
{
    struct ether_header *eth_header;
    struct ip *ip_header;
    struct icmphdr *icmp_header;
    char src_ip[INET_ADDRSTRLEN];
    char dst_ip[INET_ADDRSTRLEN];
    
    // 跳过以太网头部(14字节)
    eth_header = (struct ether_header *)packet;
    if (ntohs(eth_header->ether_type) != ETHERTYPE_IP) return; // 不是IP包，忽略
    
    // 获取IP头部
    ip_header = (struct ip *)(packet + sizeof(struct ether_header));
    
    // 检查是否为ICMP协议
    if (ip_header->ip_p != IPPROTO_ICMP) return; // 不是ICMP包，忽略
    
    // 获取ICMP头部
    icmp_header = (struct icmphdr *)(packet + sizeof(struct ether_header) + (ip_header->ip_hl << 2));
    
    // 检查是否为ICMP ECHO请求(ping)
    if (icmp_header->type != ICMP_ECHO) return; // 不是ECHO请求，忽略
    
    // 转换IP地址为可读格式
    inet_ntop(AF_INET, &(ip_header->ip_src), src_ip, INET_ADDRSTRLEN);
    inet_ntop(AF_INET, &(ip_header->ip_dst), dst_ip, INET_ADDRSTRLEN);
    
    // 打印捕获的信息
    printf("\n捕获到ICMP ECHO请求(ping)包\n");
    printf("捕获时间: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));
    printf("协议类型: ICMP (1)\n");
    printf("源IP地址: %s\n", src_ip);
    printf("目标IP地址: %s\n", dst_ip);
    printf("IP头部长度: %d 字节\n", ip_header->ip_hl << 2);
    printf("数据包总长度: %d 字节\n", ntohs(ip_header->ip_len));
    printf("TTL: %d\n", ip_header->ip_ttl);
    printf("ICMP类型: ECHO请求(%d)\n", icmp_header->type);
    printf("ICMP代码: %d\n", icmp_header->code);
    printf("ICMP校验和: 0x%04x\n", ntohs(icmp_header->checksum));
    printf("ICMP标识符: %d\n", ntohs(icmp_header->un.echo.id));
    printf("ICMP序列号: %d\n", ntohs(icmp_header->un.echo.sequence));
    printf("----------------------------------------\n");
}

int main(int argc, char *argv[])
{
    pcap_t *handle;
    char errbuf[PCAP_ERRBUF_SIZE];
    char *dev;
    struct bpf_program fp;
    char filter_exp[] = "icmp and icmp[0] == 8"; // 过滤ICMP ECHO请求
    bpf_u_int32 net;
    int count = 0;
    int max_packets = -1; // 默认无限捕获
    
    // 解析命令行参数
    if (argc == 2) max_packets = atoi(argv[1]);
    
    // 查找可用的网络设备
    dev = pcap_lookupdev(errbuf);
    if (dev == NULL)
	{
        fprintf(stderr, "找不到网络设备: %s\n", errbuf);
        return 1;
    }
    
    printf("使用网络设备: %s\n", dev);
    printf("过滤器: %s\n", filter_exp);
    
    // 打开网络设备进行捕获
    handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf);
    if (handle == NULL)
	{
        fprintf(stderr, "无法打开设备 %s: %s\n", dev, errbuf);
        return 1;
    }
    
    // 只捕获以太网流量
    if (pcap_datalink(handle) != DLT_EN10MB)
	{
        fprintf(stderr, "设备 %s 不提供以太网头\n", dev);
        pcap_close(handle);
        return 1;
    }
    
    // 编译和设置过滤器
    if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1)
	{
        fprintf(stderr, "无法解析过滤器 %s: %s\n", filter_exp, pcap_geterr(handle));
        pcap_close(handle);
        return 1;
    }
    
    if (pcap_setfilter(handle, &fp) == -1)
	{
        fprintf(stderr, "无法安装过滤器 %s: %s\n", filter_exp, pcap_geterr(handle));
        pcap_close(handle);
        return 1;
    }
    
    printf("开始捕获ICMP ECHO请求(ping)包...\n");
    printf("按Ctrl+C停止捕获\n");
    
    // 开始捕获数据包
    if (max_packets > 0) pcap_loop(handle, max_packets, icmp_packet_handler, (u_char *)&count);
    else pcap_loop(handle, -1, icmp_packet_handler, (u_char *)&count);
    
    // 清理资源
    pcap_freecode(&fp);
    pcap_close(handle);
    
    return 0;
}